package com.yunlongn.async.log.impl;

import com.yunlongn.async.function.RetryScheduleAction;
import com.yunlongn.async.log.LogDo;
import com.yunlongn.async.log.RetryScheduleLogService;
import com.yunlongn.async.util.IpAndProcessIdUtils;
import com.yunlongn.async.util.RetryScheduleLogger;
import com.yunlongn.common.json.JsonUtils;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * 日记服务基类
 * @author yunlongn
 */
public abstract class AbstractScheduleLog implements RetryScheduleLogService {

    private final static ZoneOffset ZONE_OFFSET = ZoneId.systemDefault().getRules().getOffset(Instant.now());

    @Override
    public <T> void taskFirstSubmit(String id, RetryScheduleAction<T> action, int... freePeriods) {
        LogDo entity = new LogDo();
        entity.setId(id);
        entity.setTaskName(action.getTaskName());
        if (action.getParam() != null) {
            entity.setParamType(action.getParamType());
            entity.setParam(JsonUtils.toJsonString(action.getParam()));
        }
        List<Integer> periods = new ArrayList<>();
        for (int p : freePeriods) {
            periods.add(p);
        }
        LocalDateTime cur = LocalDateTime.now();
        entity.setFreePeriods(periods);
        entity.setFirstSubmitDate(cur.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        entity.setRetryCnt(0);
        entity.setStatus(LogDo.Stat.PENDING.getStat());
        entity.setNextRetryTime(cur.toInstant(ZONE_OFFSET).toEpochMilli());
        entity.setClient(IpAndProcessIdUtils.getLocalAddressAndPid());
        RetryScheduleLogger.info("RetryScheduleLogService task first submit = {}", entity);
        this.doSava(entity);
    }


    @Override
    public void taskRetrySubmit(String id) {
        LogDo entity = this.doQuery(id);
        if (entity == null) {
            return;
        }
        entity.setRetryCnt(entity.getRetryCnt() == null ? 1 : entity.getRetryCnt() + 1);
        LocalDateTime cur = LocalDateTime.now();
        entity.setStatus(LogDo.Stat.PENDING.getStat());
        int nextPeriodSeconds = entity.getFreePeriods().get(entity.getRetryCnt() - 1);
        entity.setNextRetryTime(cur.plusSeconds(nextPeriodSeconds).toInstant(ZONE_OFFSET).toEpochMilli());
        entity.setClient(IpAndProcessIdUtils.getLocalAddressAndPid());
        RetryScheduleLogger.info("任务重新提交：{}", entity);
        this.doUpdate(entity);
    }

    @Override
    public void taskTailFail(String id) {
        LogDo entity = doQuery(id);
        if (entity == null) {
            return;
        }
        entity.setStatus(LogDo.Stat.FAIL.getStat());
        entity.setNextRetryTime(-1);
        RetryScheduleLogger.warn("任务调度执行失败：{}", entity);
        this.doUpdate(entity);
    }

    @Override
    public void taskSuccess(String id) {
        LogDo entity = doQuery(id);
        if (entity == null) {
            return;
        }
        entity.setStatus(LogDo.Stat.SUCCESS.getStat());
        entity.setNextRetryTime(-1);
        RetryScheduleLogger.info("任务调度执行成功：{} ", entity);
        this.doUpdate(entity);
    }




    @Override
    public LogDo findLog(String id) {
        return this.doQuery(id);
    }
    /**
     * 保存数据
     * @param logDo 日记
     */
    protected abstract void doSava(LogDo logDo);


    /**
     * 查询日记
     *
     * @param id 任务id
     * @return
     */
    protected abstract LogDo doQuery(String id);

    /**
     * 更新日记
     *
     * @param entity id不能为空
     */
    protected abstract void doUpdate(LogDo entity);

}
